package org.jprobe.reflect.classpath;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.jprobe.JProbeException;

public interface InstantiatorInterface {

	/** indicates searching for static methods  **/
	public static final char STATIC_ONLY_MODIFIER = 's';
	/** indicates searching for non static methods  **/
	public static final char NON_STATIC_MODIFIER = 'n';
	/** indicates searching for static/non-static methods  **/
	public static final char ANY_MODIFIER = 'a';

	/**
	 * <p>determines if the class is initializable or not by testing the
	 * accessibility of the local declared constructors, if all of which
	 * fails, then try to find a static method which returns the same type
	 * as the class itself acting as a singleton pattern.<p>
	 * <p>Arrays are considered intializable, despite being of primitive
	 * or reference type</p>
	 *
	 * @param clazz the class to be tested if can be instantiated
	 * @return true if there exist a way to make the object initializable,
	 * 			false otherwise
	 * @throws ProbeException
	 */
	boolean isInitializable(final Class<?> clazz) throws Exception;
	/**
	 * implements abstract methods in an abstract class and returns an
	 * initialized version of the required class type
	 *
	 * @param clazz the abstract class to be initialized
	 * @return an initialized object
	 * @throws Exception if the initialization is impossible
	 */
	Object getInitializedObject(final Class<?> clazz) throws Exception;
	/**
	 * searches for the first public constructor, either declared by the same class, or by one of any subclass
	 * @param clazz the class to be initialized
	 * @return a constructor, or null if none of the subclasses has a public constructor
	 * @throws ClassNotFoundException if calling find subclasses fails
	 */
	Constructor<?> getFirstPublicConstructor(final Class<?> clazz);

	/**
	 * scans the given object for <code>PUBLIC</code> methods returning an initialized state of the object
	 *
	 * @param initializer the object which contains a static method that returns the same object type
	 * @param initializeWhat the class type
	 * @return array of methods which initializes an object
	 */
	List<Method> getInitializers(final Class<?> initializeWhat,
			final Class<?> initializer, final char modifierType);

	/**
	 * gets an access to classes with no constructors by bypassing security check imposed by
	 * the private modifier on the default empty constructor
	 *
	 * @param clazz the class to be initialized
	 * @return an object
	 *
	 * @throws SecurityException if constructor can not be accessed
	 * @throws NoSuchMethodException if constructor is not defined
	 * @throws IllegalArgumentException if the argument supplied is invalid
	 * @throws InstantiationException if the constructor throws an exception while intantiation
	 * @throws IllegalAccessException if bypassing private fails
	 * @throws InvocationTargetException if invocation is not permitted
	 */
	Object getPrivateEmptyConstructorByPassed(final Class<?> clazz) throws Exception;

	/**
	 * gets an array of dummy initialized parameters
	 *
	 * @param accessibleObject a Method/Constructor
	 * @return array of null parameters
	 * @throws Exception
	 */
	Object[] getNullInitializedParameters(final AccessibleObject accessibleObject) throws Exception;

	/**
	 * gets a list of the methods and constructors that can make the object (clazz) initializable,
	 * clazz can be interface, abstract or a normal class. The methods finds its public declared constructors
	 * and any method that is declared in the project of return type which is the same as the object (clazz),
	 * or static methods declared in a factory class that return the same type
	 *
	 * @param clazz is the object to find the methods that can make it ready for use
	 * @return list of methods/constructors
	 * @throws ClassNotFoundException if the class is not available in the classpath
	 */
	List<AccessibleObject> getClassInitilizers(final Class<?> clazz) throws ClassNotFoundException;

	/**
	 * gets a set of all of the constructors which are accessible
	 * from the same class or any subclass
	 * @param clazz the class to get public constructors from
	 * @return set of constructors
	 */
	Collection<Constructor<?>> getPublicConstructors(final Class<?> clazz);

	/**
	 * gets a list of methods which initialize the class <code>clazz</code> in a given package
	 *
	 * @param pckg the package containing initializing classes if any
	 * @param clazz the object to explore
	 * @return list of methods of <code>clazz</code> return type
	 * @throws ClassNotFoundException if searching the local class path caused it
	 */
	Set<AccessibleObject> getClassInitilizers(final String pckg, final Class<?> clazz) throws ClassNotFoundException;
	/*
	 * gets a new instance of a given class type (including primitives),
	 * this method returns <code>null</code>, if it fails to initializes it;
	 * @param clazz the class to find a method/constructor that initializes it
	 * @return a new instance of an object
	 * @throws JProbeException if the initialization is impossible
	 */
//	Object getNewInstanceOf(Class<?> clazz) throws JProbeException;
	/**
	 * gets a dummy list of invocation parameters to be used in method/constructor invocation
	 * @param parameterTypes array of each parameter type
	 * @return array of initialized objects
	 * @throws JProbeException if any parameter can not be initialized
	 */
	Object[] getParametersValues(final Class<?>[] parameterTypes)throws Exception;
	/**
	 * scans the given object for methods declared as static and returns an initialized state
	 * of the object as in the singleton pattern
	 *
	 * @param clazz the object which contains a static method that returns the same object type
	 * @return array of methods which initializes an object
	 */
	List<Method> getFactoryCreatorMethod(final Class<?> clazz);	
	/**
	 * determines wether or not, class has its empty constructor declared public, or just left not declared
	 * given that no other non-empty constructors were declared
	 * @param clazz a class
	 * @return true iff the class has its own empty constructor available
	 */
	boolean hasPublicEmptyConstructor(final Class<?> clazz);
	/**
	 * determines if a class has no public constructors at all
	 * @param clazz a class object
	 * @return true iff the object does not have public constructoirs declared
	 */
	boolean hasNoPublicConstructors(final Class<?> clazz);
	/**
	 * loads a class using a similar mechanism as <code>Class.forName</code>
	 * @param className the binary name of the class 
	 * @return a class object
	 * @throws ClassNotFoundException if the class can not be found in the classpath
	 */
	Class<?> loadClass(String className) throws ClassNotFoundException;



}